/*	COMP 6761 Assignment 1 Framework
	Created by Kaustubha Mendhurwar on 09/09/14.
	Copyright (c) 2014 Concordia University. All rights reserved.
*/

#include "ArcBall.h"
#include <algorithm>

ArcBall::ArcBall(int window_width, int window_height, GLfloat roll_speed, bool x_axis, bool y_axis)
{
	ArcBall::windowWidth = window_width;
	ArcBall::windowHeight = window_height;

	ArcBall::mouseEvent = 0;
	ArcBall::rollSpeed = roll_speed;
	ArcBall::angle = 0.0f;
	ArcBall::camAxis = glm::vec3(0.0f, 1.0f, 0.0f);

	ArcBall::xAxis = x_axis;
	ArcBall::yAxis = y_axis;
}

ArcBall::~ArcBall()
{
}

/*Convert the mouse cursor coordinate from screen space (i.e. from (0,0) to (windowWidth, windowHeight))
  into normalized screen coordinate (i.e. (-1, -1) to (1, 1) */
glm::vec3 ArcBall::toScreenCoordinates( double x, double y )
{
    glm::vec3 coord(0.0f);
    
    if(xAxis)
        coord.x =  (2 * x - windowWidth ) / windowWidth;
    
    if(yAxis)
        coord.y = -(2 * y - windowHeight) / windowHeight;
    
    /* Clamp it to border of the windows, comment these codes to allow rotation when cursor is not over window */
    coord.x = glm::clamp( coord.x, -1.0f, 1.0f );
    coord.y = glm::clamp( coord.y, -1.0f, 1.0f );
    
    float length_squared = coord.x * coord.x + coord.y * coord.y;

    if(length_squared <= 1.0)
        coord.z = sqrt(1.0 - length_squared);
    else
        coord = glm::normalize(coord);
    
    return coord;
}

/*Mouse Tracking
  Event 0: when no tracking needed
  Event 1: at the start of tracking, recording the first cursor pos
  Event 2: tracking of subsequent cursor movement*/
void ArcBall::mouseButtonCallback( GLFWwindow * window, int button, int action, int mods )
{
    mouseEvent = ( action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT );
}

void ArcBall::cursorCallback( GLFWwindow *window, double x, double y )
{
    if( mouseEvent == 0 )
    {
        rotationMatrix = createViewRotationMatrix();
        angle = 0;
        return;
    }

    else if( mouseEvent == 1 )
	{
        /* Start of trackball, remember the first position */
        prevPos     = toScreenCoordinates(x, y);
        mouseEvent  = 2;
        return;
    }
    
    /* Tracking the subsequent */
    currPos  = toScreenCoordinates(x, y);
    
    /* Calculate the angle in radians, and clamp it between 0 and 90 degrees */
    angle = acos(std::min(1.0f, glm::dot(prevPos, currPos)));
    
    /* Cross product to get the rotation axis, but it's still in camera coordinate */
    camAxis  = glm::cross(prevPos, currPos);
}

/* Create rotation matrix in camera coordinate space,
   multiply this matrix with view matrix to rotate the camera*/
glm::mat4 ArcBall::createViewRotationMatrix()
{
    return glm::rotate(glm::degrees(angle) * rollSpeed, camAxis) * rotationMatrix;
}

/* Create rotation matrix in the world coordinate space,
   multiply this matrix with model matrix to rotate the object*/
glm::mat4 ArcBall::createModelRotationMatrix(glm::mat4 &view_matrix )
{
    glm::vec3 axis = glm::inverse(glm::mat3(view_matrix)) * camAxis;
    return glm::rotate( glm::degrees(angle) * rollSpeed, axis );
}